﻿using Bigger.Study.DataEntity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace Bigger.Study.ConsoleApp
{
    public class EmitExample : IExample
    {
        public void Execute()
        {
            //Course objCourse = new Course();
            //objCourse.CourseTitle = "课程标题";

            //PropertyInfo propertyInfo = typeof(Course).GetProperty("CourseTitle");

            //GetValueDelegate getValueDelegate = DynamicMethodFactory.CreatePropertyGetter(propertyInfo);
            //Console.WriteLine(getValueDelegate(objCourse));

            //SetValueDelegate setValueDelegate= DynamicMethodFactory.CreatePropertySetter(propertyInfo);
            //setValueDelegate(objCourse, "课程标题1111");
            //Console.WriteLine(objCourse.CourseTitle);

            TestCourse objTestCourse = new TestCourse();
            objTestCourse.StudentCount = 20;

            PropertyInfo propertyInfo = typeof(TestCourse).GetProperty("StudentCount");

            GetValueDelegate getValueDelegate = DynamicMethodFactory.CreatePropertyGetter(propertyInfo);
            Console.WriteLine(getValueDelegate(objTestCourse));

            SetValueDelegate setValueDelegate = DynamicMethodFactory.CreatePropertySetter(propertyInfo);
            setValueDelegate(objTestCourse, 30);
            Console.WriteLine(objTestCourse.StudentCount);
        }

        public class TestCourse
        {
            public string CourseTitle { get; set; }
            public int? StudentCount { get; set; }
        }
    }

    public delegate void SetValueDelegate(object target, object arg);
    public delegate object GetValueDelegate(object target);

    public static class DynamicMethodFactory
    {
        //定义InstantiateObjectHandler委托
        public delegate object InstantiateObjectHandler();


        // 创建InstantiateObject委托
        public static InstantiateObjectHandler CreateInstantiateObjectHandler(Type type)
        {
            ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public |
                   BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null);

            if (constructorInfo == null)
            {
                throw new ApplicationException(string.Format("The type {0} must declare an empty constructor (the constructor may be private, internal, protected, protected internal, or public).", type));
            }

            DynamicMethod dynamicMethod = new DynamicMethod("InstantiateObject",
                    MethodAttributes.Static |
                  MethodAttributes.Public, CallingConventions.Standard, typeof(object),
                    null, type, true);

            ILGenerator generator = dynamicMethod.GetILGenerator();
            generator.Emit(OpCodes.Newobj, constructorInfo);
            generator.Emit(OpCodes.Ret);
            return (InstantiateObjectHandler)dynamicMethod.CreateDelegate
                    (typeof(InstantiateObjectHandler));
        }

        // 创建Get委托
        public static GetValueDelegate CreatePropertyGetter(PropertyInfo propertyInfo)
        {
            MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true);
            DynamicMethod dynamicGet = CreateGetDynamicMethod(propertyInfo.DeclaringType);
            ILGenerator getGenerator = dynamicGet.GetILGenerator();

            if (!getMethodInfo.IsStatic)
            {
                getGenerator.Emit(OpCodes.Ldarg_0);
            }
            //getGenerator.Emit(OpCodes.Ldarg_0);
            getGenerator.Emit(OpCodes.Call, getMethodInfo);
            BoxIfNeeded(getMethodInfo.ReturnType, getGenerator);
            getGenerator.Emit(OpCodes.Ret);

            return (GetValueDelegate)dynamicGet.CreateDelegate(typeof(GetValueDelegate));
        }

        // 创建Get委托
        public static GetValueDelegate CreateFieldGetter(FieldInfo fieldInfo)
        {
            DynamicMethod dynamicGet = CreateGetDynamicMethod(fieldInfo.DeclaringType);
            ILGenerator getGenerator = dynamicGet.GetILGenerator();

            if (!fieldInfo.IsStatic)
            {
                getGenerator.Emit(OpCodes.Ldarg_0);
            }
            //getGenerator.Emit(OpCodes.Ldarg_0);
            getGenerator.Emit(OpCodes.Ldfld, fieldInfo);
            BoxIfNeeded(fieldInfo.FieldType, getGenerator);
            getGenerator.Emit(OpCodes.Ret);

            return (GetValueDelegate)dynamicGet.CreateDelegate(typeof(GetValueDelegate));
        }

        // 创建Set委托
        public static SetValueDelegate CreatePropertySetter(PropertyInfo propertyInfo)
        {
            MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true);
            DynamicMethod dynamicSet = CreateSetDynamicMethod(propertyInfo.DeclaringType);
            ILGenerator setGenerator = dynamicSet.GetILGenerator();

            if (!setMethodInfo.IsStatic)
            {
                setGenerator.Emit(OpCodes.Ldarg_0);
            }
            //getGenerator.Emit(OpCodes.Ldarg_0);
            setGenerator.Emit(OpCodes.Ldarg_1);
            UnboxIfNeeded(setMethodInfo.GetParameters()[0].ParameterType, setGenerator);
            setGenerator.Emit(OpCodes.Call, setMethodInfo);
            setGenerator.Emit(OpCodes.Ret);

            return (SetValueDelegate)dynamicSet.CreateDelegate(typeof(SetValueDelegate));
        }

        // 创建Set委托
        public static SetValueDelegate CreateFieldSetter(FieldInfo fieldInfo)
        {
            DynamicMethod dynamicSet = CreateSetDynamicMethod(fieldInfo.DeclaringType);
            ILGenerator setGenerator = dynamicSet.GetILGenerator();

            if (!fieldInfo.IsStatic)
            {
                setGenerator.Emit(OpCodes.Ldarg_0);
            }
            //getGenerator.Emit(OpCodes.Ldarg_0);
            setGenerator.Emit(OpCodes.Ldarg_1);
            UnboxIfNeeded(fieldInfo.FieldType, setGenerator);
            setGenerator.Emit(OpCodes.Stfld, fieldInfo);
            setGenerator.Emit(OpCodes.Ret);

            return (SetValueDelegate)dynamicSet.CreateDelegate(typeof(SetValueDelegate));
        }

        // 创建Get动态方法
        private static DynamicMethod CreateGetDynamicMethod(Type type)
        {
            return new DynamicMethod("DynamicGet", typeof(object),
                  new Type[] { typeof(object) }, type, true);
        }

        // 创建Set动态方法
        private static DynamicMethod CreateSetDynamicMethod(Type type)
        {
            return new DynamicMethod("DynamicSet", typeof(void),
                  new Type[] { typeof(object), typeof(object) }, type, true);
        }

        // BoxIfNeeded
        private static void BoxIfNeeded(Type type, ILGenerator generator)
        {
            if (type.IsValueType)
            {
                generator.Emit(OpCodes.Box, type);
            }
        }

        // UnboxIfNeeded
        private static void UnboxIfNeeded(Type type, ILGenerator generator)
        {
            if (type.IsValueType)
            {
                generator.Emit(OpCodes.Unbox_Any, type);
            }
        }

    }
}
